# (c) Tashan and Friends 2010

# importing things
from random import gauss
from pylab import zeros


class Traders():
    """This is a base class for traders, which define some common properties to all the traders
    """
    def __init__(self):
        self.Pbar = 100.0       # Long-Run Fundamental Price
        self.r = 5.0/100        # risk free asset rate per annum
        self.sigma = 20.0/100   # sigma
        self.K = 250            # Frequency of trading period per year, K = 1,12,52,250 for a year,month,week,day
        self.Pt = self.Pbar     # Initial price
        self.R = 1 + self.r/self.K
        self.sigma1sq = ((self.Pbar*self.sigma)**2.0)/self.K
        self.sigmadsq = (self.sigma1sq)*(self.r)**2.0


class  Fundamentalist(Traders):
    """ Fundamentalist have more knowledge of the market and believe that may be price can oscillate, but finally it will convege to their predicted value.
	This class define a typical fundamentalist. Have a look at all the parameter to get a feel of it.
    """
    def setup(self):
	""" Setup fundamentalist"""
        self.alpha = 0.8          # 
        self.a1 = .8              # Utility Constant
        self.Pt = 80.0            # Initial price
        self.mu01 = (2*self.a1*(1+self.r**2)*self.sigma1sq)/(self.R-self.alpha)  # Some stability factor
	# mu < mu01 for a stable solution
        self.mu = 1.0
    

    def calcDemand(self):
	"""Calculate the demand of fundamentalist with given market scenario"""
        Pt = self.Pt
        alpha = self.alpha
        R = self.R
        a1 = self.a1
        sigma1sq = self.sigma1sq
        r = self.r
        Pbar = self.Pbar     # Mean value expected
        Pstar = Pbar + gauss(0,5)  # Fundamentalist expectation
        num = (alpha-1)*(Pt-Pstar) - (R-1)*(Pt-Pbar)
        den = a1*(1+r**2)*(sigma1sq)
        self.ze = num/den
        return self.ze   
    
    def calcFutureVal(self):
	"""Calculate the future value of Prices because of fundamentalist depending on the current market scenario"""
        Pt = self.Pt
        mu = self.mu
        sigma1sq = self.sigma1sq
        epsbar = gauss(0,0)
        self.Pt = Pt + mu*self.ze + epsbar
        return self.Pt
    

class  TrendFollower(Traders):
    """Same as Fundamentalist"""
    def setup(self):
        self.gamma = .05
        self.a2 = 1.0
        self.b = 1
        self.partial = .9
        self.ut = 0.0
        self.vt = 0.0
        self.Pt =99.0
        self.mu = .14
    def calcDemand(self):
        Pt = self.Pt
        partial = self.partial
        gamma = self.gamma
        R = self.R
        a2 = self.a2
        sigma1sq = self.sigma1sq
        r = self.r
        Pbar = self.Pbar
        b = self.b

        ut = partial*self.ut + (1-partial)*Pt
        vt = partial*self.vt + partial*(1-partial)*(Pt-self.ut)**2
        self.ut = ut
        self.vt = vt
        num = gamma*(Pt-ut) - (R-1)*(Pt-Pbar)
        den = a2*(1 + r**2 + b*vt)*(sigma1sq)
        self.ze = num/den
        return self.ze

    
    def calcFutureVal(self):
        Pt = self.Pt
        mu = self.mu
        sigma1sq = self.sigma1sq
        epsbar = gauss(0,0.05)
        self.Pt = Pt + mu*self.zet + epsbar
        return self.Pt
       
class  market():
    """Define a typical market, see the code for details, presentaly it contains all trendfollower"""
    def setup(self):
	self.n = 100	
	n = self.n
	traders = []
	for i in range(n):
    	    trader = TrendFollower()
     	    trader.setup()
            trader.partial = trader.partial + gauss(0,.005)
            trader.a2 = trader.a2 + gauss(0,.005)
	    traders.append(trader)
	self.traders = traders
	self.val = []
	self.valval = 0
    def startBussiness(self):
	"""Calculate prices due to all the traders in the market for a given day
	"""
	traders = self.traders
	self.demand = []
	zet = 0
	for i in range(self.n):
	    tmp	 = traders[i].calcDemand()
	    self.demand.append(tmp)
	    zet += tmp/100	
	tmp = 0
	self.expec = []
       	for i in range(100):
	     traders[i].zet = zet
	     self.expec.append(traders[i].calcFutureVal())
	     tmp += self.expec[i]
	self.val.append(tmp)
		
	
	
